package main;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;

import aisLogic.Manager;
import aisLogic.Module;

public class AISCanvasPanel extends JPanel implements Observer, MouseInputListener, MouseWheelListener{
	
	private final int SCALE_MAX = 10;
	private final int SCALE_MIN = 1;
	private int fixed_x = 0;
	private int fixed_y = 0;
	private int x = 0;
	private int y = 0;
	private int h = 0;
	private int w = 0;
	private int mod_x = 0;
	private int mod_y = 0;
	private int mod_w = 0;
	private int mod_h = 0;
	private Image img = null;
	private Image scaledImage = null;
	private int scaleIndex = SCALE_MIN;
	private boolean editModule = false;
	private boolean moveModule = false;
	private int editFlag = 0;
	final static int NO_BORDER = 0;
	final static int LEFT_BORDER = 1 << 0;
	final static int RIGHT_BORDER = 1 << 1;
	final static int BOTTOM_BORDER = 1 << 2;
	final static int UPPER_BORDER = 1 << 3;
	
	public AISCanvasPanel(String arg0){
		img = Toolkit.getDefaultToolkit().getImage(arg0);
		scaledImage = Toolkit.getDefaultToolkit().getImage(arg0);
		addMouseListener(this);
		addMouseMotionListener(this);
		addMouseWheelListener(this);
		AthenaMain.manager.addObserver(this);
	}
	
	public void mouseClicked(MouseEvent arg0) {
		if(MouseEvent.BUTTON2 == arg0.getButton()){
			AthenaMain.uinterface.canvasScrollPane.moveViewport(arg0.getPoint());
		}
	}

	public void mousePressed(MouseEvent arg0){
		int button = arg0.getButton();
		if(MouseEvent.BUTTON1 == button){
			fixed_x = arg0.getX();
			fixed_y = arg0.getY();
			w = 0;
			h = 0;
		} else if(MouseEvent.BUTTON3 == button){
			if(moveModule){
				fixed_x = (arg0.getX() / scaleIndex) - mod_x;
				fixed_y = (arg0.getY() / scaleIndex) - mod_y;
			} else if(editModule){
				
			}
		}
	}

	public void mouseReleased(MouseEvent arg0) {
		if(MouseEvent.BUTTON1 == arg0.getButton()){
			if(w != 0 && h != 0){
				AthenaMain.manager.addModule(new Module(x / scaleIndex, y / scaleIndex, w / scaleIndex, h / scaleIndex));
			}
			resetRect();
		} else if(MouseEvent.BUTTON3 == arg0.getButton()){
			int module_id = AthenaMain.uinterface.moduleScrollPane.getTable().getSelectedRow();
			if(moveModule){
				int final_x = mod_x;
				int final_y = mod_y;
				AthenaMain.manager.moduleUpdate(module_id, Module.ATT_Y, final_y);
				AthenaMain.manager.moduleUpdate(module_id, Module.ATT_X, final_x);
				AthenaMain.uinterface.moduleScrollPane.getTable().setValueAt(final_y, module_id, Module.ATT_Y);
				AthenaMain.uinterface.moduleScrollPane.getTable().setValueAt(final_x, module_id, Module.ATT_X);
			} else if(editModule){
				int final_x = mod_x;
				int final_y = mod_y;
				int final_w = mod_w;
				int final_h = mod_h;
				AthenaMain.manager.moduleUpdate(module_id, Module.ATT_X, final_x);
				AthenaMain.manager.moduleUpdate(module_id, Module.ATT_Y, final_y);					
				AthenaMain.manager.moduleUpdate(module_id, Module.ATT_WIDTH, final_w);					
				AthenaMain.manager.moduleUpdate(module_id, Module.ATT_HEIGHT, final_h);
				AthenaMain.uinterface.moduleScrollPane.getTable().setValueAt(final_x, module_id, Module.ATT_X);
				AthenaMain.uinterface.moduleScrollPane.getTable().setValueAt(final_y, module_id, Module.ATT_Y);
				AthenaMain.uinterface.moduleScrollPane.getTable().setValueAt(final_w, module_id, Module.ATT_WIDTH);
				AthenaMain.uinterface.moduleScrollPane.getTable().setValueAt(final_h, module_id, Module.ATT_HEIGHT);					
			}
		}
		updateUI();
	}

	public void mouseEntered(MouseEvent arg0) {
	}

	public void mouseExited(MouseEvent arg0) {
	}

	public void mouseDragged(MouseEvent arg0) {
		int mouse_x = arg0.getX();
		int mouse_y = arg0.getY();
		
		if((arg0.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0){
			if(fixed_x < mouse_x){
				x = fixed_x - (fixed_x % scaleIndex);
				w = (mouse_x - x) - ((mouse_x - x) % scaleIndex);
				w = Math.min(w, (596 * scaleIndex) - x);
			} else {
				x = mouse_x - (mouse_x % scaleIndex);
				x = Math.max(x, 0);
				w = (fixed_x - x) - ((fixed_x - x) % scaleIndex);
			}
			if(fixed_y < mouse_y){
				y = fixed_y - (fixed_y % scaleIndex);
				h = (mouse_y - y) - ((mouse_y - y) % scaleIndex);
				h = Math.min(h, (546 * scaleIndex) - y);
			} else {
				y = mouse_y - (mouse_y % scaleIndex);
				y = Math.max(y, 0);
				h = (fixed_y - y) - ((fixed_y - y) % scaleIndex);
			}
		} else if((arg0.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0){
			if(moveModule){
				mod_x = Math.max((mouse_x / scaleIndex) - fixed_x, 0);
				mod_y = Math.max((mouse_y / scaleIndex) - fixed_y, 0);
				mod_x = Math.min(mod_x, 596 - mod_w);
				mod_y = Math.min(mod_y, 546 - mod_h);
			} else if(editModule){
				if((editFlag & RIGHT_BORDER) != 0){
					mod_w = (mouse_x / scaleIndex) - mod_x;
				} else if((editFlag & LEFT_BORDER) != 0){
					mod_w -= (mouse_x / scaleIndex) - mod_x;
					mod_x = mouse_x / scaleIndex;
				}
				if((editFlag & BOTTOM_BORDER) != 0){
					mod_h = (mouse_y / scaleIndex) - mod_y;
				} else if((editFlag & UPPER_BORDER) != 0){
					mod_h -= (mouse_y / scaleIndex) - mod_y;
					mod_y = mouse_y / scaleIndex;					
				}
			}
		}
		updateUI();
	}

	public boolean insideRectangle(int x, int y){
		int border = 5;
		int x1 = mod_x * scaleIndex;
		int y1 = mod_y * scaleIndex;
		int x2 = (mod_w + mod_x) * scaleIndex;
		int y2 = (mod_h + mod_y) * scaleIndex;
		
		if(x < x2 - border && x > x1 + border && y < y2 - border && y > y1 + border){
			return true;
		}
		return false;
	}
	
	public int onBorder(int x, int y){
		int flag = 0;
		int border = 5;
		int x1 = mod_x * scaleIndex;
		int y1 = mod_y * scaleIndex;
		int x2 = (mod_w + mod_x) * scaleIndex;
		int y2 = (mod_h + mod_y) * scaleIndex;
		
		if(x > x1 - border && x < x1 + border && y > y1 && y < y2){
			flag |= LEFT_BORDER;
		} else if(x > x2 - border && x < x2 + border && y > y1 && y < y2){
			flag |= RIGHT_BORDER;
		} 
		if(y > y1 - border && y < y1 + border && x > x1 && x < x2){
			flag |= UPPER_BORDER;
		} if(y > y2 - border && y < y2 + border && x > x1 && x < x2){
			flag |= BOTTOM_BORDER;
		}
		return flag;
	}
	
	public void mouseMoved(MouseEvent arg0){
		int x = arg0.getX();
		int y = arg0.getY();
		editModule = false;
		moveModule = false;
		editFlag = onBorder(x, y);
		
		if(insideRectangle(x, y)){
			moveModule = true;
			setCursor(new Cursor(Cursor.MOVE_CURSOR));
		} else if(editFlag != 0){
			int cursor = 0;
			editModule = true;
			switch(editFlag){
				case LEFT_BORDER:
				case RIGHT_BORDER:
					cursor = Cursor.W_RESIZE_CURSOR;
					break;
				case LEFT_BORDER | UPPER_BORDER:
				case RIGHT_BORDER | BOTTOM_BORDER:
					cursor = Cursor.NW_RESIZE_CURSOR;
					break;
				case LEFT_BORDER | BOTTOM_BORDER:
				case RIGHT_BORDER | UPPER_BORDER:
					cursor = Cursor.NE_RESIZE_CURSOR;
					break;
				case UPPER_BORDER:
				case BOTTOM_BORDER:
					cursor = Cursor.N_RESIZE_CURSOR;
					break;
			}
			setCursor(new Cursor(cursor));
		} else {
			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
		}
	}
	
	public void paintComponent(Graphics g){
		if(scaledImage != null){
			g.setColor(new Color(0));
			g.fillRect(0, 0, getWidth(), getHeight());
			g.drawImage(scaledImage, 0, 0, this);
			if(w != 0 && h != 0){
				g.setColor(new Color(0x0000FF00));
				g.drawRect(x, y, w, h);
			}
			if(mod_w != 0 && mod_h != 0){
				g.setColor(new Color(0x00FF0000));
				g.drawRect(mod_x * scaleIndex, mod_y * scaleIndex, mod_w * scaleIndex, mod_h * scaleIndex);
			}
		}
	}

	public Image getImage() {
		return img;
	}
	
	public void mouseWheelMoved(MouseWheelEvent arg0) {
		if(arg0.getWheelRotation() < 0){
			if(scaleIndex < SCALE_MAX){
				scaleIndex++;
				int width = img.getWidth(this);
				int height = img.getHeight(this);
				scaledImage = img.getScaledInstance(width * scaleIndex, height * scaleIndex, Image.SCALE_FAST);
				setPreferredSize(new Dimension(width * scaleIndex, height * scaleIndex));
				revalidate();
				updateUI();
			}
		} else {
			if(scaleIndex > SCALE_MIN){
				scaleIndex--;
				int width = img.getWidth(this);
				int height = img.getHeight(this);
				scaledImage = img.getScaledInstance(width * scaleIndex, height * scaleIndex, Image.SCALE_FAST);
				setPreferredSize(new Dimension(width * scaleIndex, height * scaleIndex));
				revalidate();
				updateUI();
			}
		}
	}

	public void update(Observable arg0, Object arg1) {
		Object[] objs = (Object[])arg1;
		int action = ((Integer)objs[Manager.ACTION]).intValue();
		
		switch(action){
			case Manager.MODULE_ADD:
				break;
			case Manager.MODULE_REMOVE:
				break;
			case Manager.MODULE_UPDATE:
				mod_x = ((Module)objs[Manager.OBJECT]).getX();
				mod_y = ((Module)objs[Manager.OBJECT]).getY();
				mod_w = ((Module)objs[Manager.OBJECT]).getWidth();
				mod_h = ((Module)objs[Manager.OBJECT]).getHeight();
				break;
			case Manager.MODULE_DOWN:
				break;
			case Manager.MODULE_UP:
				break;
		}
		updateUI();
	}

	public void setSelectedRect(Module selectedModule) {
		mod_x = selectedModule.getX();
		mod_y = selectedModule.getY();
		mod_w = selectedModule.getWidth();
		mod_h = selectedModule.getHeight();
		updateUI();
	}
	
	public void resetRect(){
		x = 0;
		y = 0;
		h = 0;
		w = 0;
	}
}
